---
layout: main
title: Search
---
<link rel="stylesheet" href="{{site.baseurl}}/css/search.css">
<main class="container">
    <!-- Search terms -->
    <h1 id="searching-for"></h1>
    <!-- Tabs for small screens -->
    <ul class="nav nav-tabs d-md-none" role="tablist">
        <li class="nav-item">
            <a class="nav-link active" href="#entities" aria-controls="entities" data-toggle="tab">
                Entity Results
            </a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="#members" aria-controls="members" data-toggle="tab">
                Member Results
            </a>
        </li>
    </ul>
    <!-- Content -->
    <div class="tab-content row">
        <div id="entities" class="tab-pane active col-12 col-md-6" role="tabpanel">
            <h2 class="d-none d-md-block">Entity Results</h2>
            <ul id="entity-results"></ul>
        </div>
        <div id="members" class="tab-pane col-12 col-md-6" role="tabpanel">
            <h2 class="d-none d-md-block">Member Results</h2>
            <ul id="member-results"></ul>
        </div>
    </div>
</main>

<script>
    // Parse parameters from URL:
    var parameters = [];
    var apiSearch = undefined;
    var docs = {{ searchableDocs | json }};
    (function() {
        var pairs = location.search.substring(1).split('&');
        for (var i = 0; i < pairs.length; i++) {
            var keyVal = pairs[i].split('=');
            parameters[keyVal[0]] = keyVal.length == 2 ? keyVal[1] : undefined;
        }
    })();

    // Set search term and title:
    var searchTerm = decodeURIComponent(parameters["searchTerm"]);
    document.getElementById("searching-for").innerText = 'Search results for "' + searchTerm + '"';
    document.title = searchTerm + ' - Search results';

    if (!window.Worker) {
        document.getElementById("searching-for").innerText =
            "Couldn't search for \"" + searchTerm + "\", " +
            "web workers not supported. Please update your browser.";
    }
    else {
        // perform API search:
        apiSearch = new Worker("{{ site.baseurl }}/js/api-search.js");
        apiSearch.postMessage({
            "type":   "setup",
            "search": searchTerm,
            "docs":   docs,
        });

        var insertEntity = function(entityResultsNode, entityResults, suffix) {
            return function(entity, parent) {
                var shouldInsert =
                    parent.kind == "package" &&
                    !(entity.kind == "object" && entity.hasCompanion)

                if (shouldInsert) {
                    // If parent package not in list, create it first:
                    if (!(parent.name in entityResults)) {
                        var packageLi = document.createElement("li");
                        entityResultsNode.appendChild(packageLi);

                        var href = "{{ site.baseurl }}/api/" + parent.path.join('/') + "/index.html";
                        packageLi.innerHTML = "<h3 class=\"package-name\"><a href=\"" + href + "\">" + parent.name + "</a></h3>";

                        var entityUl = document.createElement("ul");
                        entityUl.classList.add("entity-ul");
                        packageLi.appendChild(entityUl);
                        entityResults[parent.name] = entityUl;
                    }

                    // Insert into list of results in package, no need to sort -
                    // already sorted:
                    var entityUl = entityResults[parent.name];
                    var selector = entity.path.slice(0, entity.path.length - 1).join('-') + '-' + entity.name + '-' + suffix;
                    var entityLi = document.querySelector('#' + selector);
                    if (!entityLi) {
                        entityLi = document.createElement("li");
                        if (entity.hasCompanion)
                            entityLi.classList.add("entity-result-li", "with-companion");
                        else
                            entityLi.classList.add("entity-result-li");

                        entityLi.id = selector;

                        var companion = !entity.hasCompanion ? "" : (
                            '<a class="letter-anchor object" href="{{ site.baseurl }}/api/' + entity.companionPath.join('/') + '.html">O</a>'
                        );

                        var letter =
                            '<a class="letter-anchor ' + entity.kind + '" href="{{ site.baseurl }}/api/' + entity.path.join('/') + '.html">' +
                            entity.kind.charAt(0).toUpperCase() +
                            '</a>'

                        entityLi.innerHTML = (
                            '<div class="entity-kinds">' +
                                companion +
                                letter +
                            '</div>' +
                            "<h4><a class=\"entity-name\" href=\"{{ site.baseurl }}/api/" + entity.path.join('/') + ".html\">" +
                            entity.name +
                            "</a></h4>"
                        );
                        entityUl.appendChild(entityLi);
                    }
                    return entityLi;
                }
                else {
                    var path = entity.path.slice(0, entity.path.length - 1)
                    return document.querySelector('#' + path + '-' + entity.name + '-' + suffix);
                }
            };
        };

        var insertMember = function(member, li, parentLink) {
            var div = document.createElement("div");
            div.classList.add("member-result");

            var renderParamList = function(plist) {
                var start = plist.isImplicit ? "(implicit " : "(";
                var args = plist.list.map(x=>x.ref.title).join(', ');
                return start + args + ")";
            };

            var concatenateStrings = function(acc, str) { return acc + str; };

            var paramLists = !member.paramLists ? "" : (
                member.paramLists
                    .map(renderParamList)
                    .reduce(concatenateStrings, "") + ':'
            );

            var returnValue = !member.returnValue ? "" : (
                member.returnValue.title
            );

            var sigObject = member.kind == "object" ? "$" : "";
            var sigParams = !member.paramLists ? "" : (
                member.paramLists.map(l => "(" + l.list.map(x=>x.title).join(",") + ")").reduce(concatenateStrings, "")
            );
            var signature = member.name + sigObject + sigParams;
            var memberLink = parentLink + "#" + signature;
            div.innerHTML =
                '<span class="member-kind">'+ member.kind +'</span>&nbsp;' +
                '<span class="member-name"><a href=\"' + memberLink + '\">' + member.name +'</a></span>' +
                '<span class="member-param-lists">' + paramLists + '</span>&nbsp;' +
                '<span class="member-return">'+ returnValue +'</span>'

            li.appendChild(div);
        };

        var entityResultsNode = document.getElementById("entity-results");
        var entityResults = [];
        var memberResultsNode = document.getElementById("member-results");
        var memberResults = [];

        apiSearch.onmessage = function(res) {
            var package = res.data.package;
            switch(res.data.type) {
                case "entityResult": {
                    var entity = res.data.entity;
                    //console.log("got entity: " + entity.name + ", in package: " + package.name);
                    insertEntity(entityResultsNode, entityResults, "entity")(entity, package);
                    break;
                }
                case "memberResult": {
                    var member = res.data.member;
                    var parent = res.data.parent;
                    //console.log("got member: " + member.name + ", in entity: " + parent.name);
                    var parentLink = "{{ site.baseurl }}/api/" + parent.path.join('/') + ".html";
                    var li = insertEntity(memberResultsNode, memberResults, "member")(parent, package);
                    if (li) insertMember(member, li, parentLink);
                    break;
                }
                default: {
                    console.log("Got unknown message: " + res.data.type);
                }
            }
        };
    }
</script>
